package com.l01.h264player

import android.content.Context
import android.media.MediaCodec
import android.media.MediaFormat
import android.util.Log
import android.view.Surface
import android.view.SurfaceView
import kotlinx.coroutines.*
import java.io.File
import java.security.AccessControlContext

class H264Player(
    private val context: Context,
    private var path: String,
    surface: Surface
) {

    //创建一个会h264的解码器
    private val mediaCodec = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_VIDEO_AVC)

    init {
        //设置解码器参数
        val mediaFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, 368, 384).apply {
            setInteger(MediaFormat.KEY_FRAME_RATE, 15)
        }
        //配置解码器关联画布用于直接输出显示
        mediaCodec.configure(mediaFormat, surface, null, 0)
    }


    fun play() {
        mediaCodec.start()
        decodeH264()
    }


    private fun decodeH264() {
        CoroutineScope(Dispatchers.IO).launch {
            var time = System.currentTimeMillis()
            //获取DSP芯片中所有的解码通道数组
            val inputBuffers = mediaCodec.inputBuffers
            //获取H264文件的二进制数据
            val mediaBytes = getBytes(path)
            var startIndex = 0
            val totalSize = mediaBytes.size
            while (true) {
                if (totalSize == 0 || startIndex >= totalSize) {
                    break
                }
                //找到下一帧（I,B,P帧）的开始位置,需跳过SPS/PPS
                val nextFrameStart = findByFrame(mediaBytes, startIndex + 2)
                //等待指定微秒后，返回正在空闲的解码通道编号
                val freeChannel = mediaCodec.dequeueInputBuffer(10000)
                if (freeChannel > 0) {
                    //找到可用的DSP解码队列
                    val channel = inputBuffers[freeChannel]
                    channel.clear()
                    //把当前帧的开始位置直到下帧开始位置前的数据提取到解码队列内
                    channel.put(mediaBytes, startIndex, nextFrameStart - startIndex)
                    //把解码队列的数据放到mediaCodec的解码通道中
                    // 参数1是可用的解码通道编号 ；参数2是通道中的码流从第几位开始解码的偏移量 ; 参数3是需要解码的长度
                    mediaCodec.queueInputBuffer(freeChannel, 0, nextFrameStart - startIndex, 0, 0)
                    //下次循环开始解码下一帧
                    startIndex = nextFrameStart
                } else {
                    continue
                }

                val bufferInfo = MediaCodec.BufferInfo()
                val outChannel = mediaCodec.dequeueOutputBuffer(bufferInfo, 10000)
                if (outChannel > 0) {
                    //避免播放太快
                    delay(30)
                    val curTime = System.currentTimeMillis() - time
                    Log.d("TAG_H264Player","curTime.... $curTime ms")
                    time = System.currentTimeMillis()
                    mediaCodec.releaseOutputBuffer(outChannel, true)
                }
            }


        }
    }

    //查找I,B,P帧的开始索引的位置
       private fun findByFrame(byteArray: ByteArray, start: Int): Int {
        for (i in start..byteArray.size - 4) {
            if (byteArray[i].toInt() == 0x00
                && byteArray[i + 1].toInt() == 0x00
                && byteArray[i + 2].toInt() == 0x00
                && byteArray[i + 3].toInt() == 0x01
            ) {
                return i
            }
        }
        return -1
    }


    private fun getBytes(path: String): ByteArray {
        return File(path).readBytes()
    }
}